﻿// Copyright (c) 2012 Andrei Rinea
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Threading;

using BenchmarkNet;

using NUnit.Framework;

namespace BenchmarkNET_Tests
{
    [TestFixture]
    public class ParallelTests
    {
        private const byte ThreadsCount = 10;
        private const int SleepMilliseconds = 1000;
        private const int MaxThreadStartupTimeMilliseconds = 15;
        private const double MaxDeviationAllowed = 1.05;

        [Test]
        public void LaunchesCorrectNumberOfThreads()
        {
            var threadIds = new List<int>();
            Benchmark.Parallel(() => threadIds.Add(Thread.CurrentThread.ManagedThreadId), ThreadsCount);
            Assert.AreEqual(ThreadsCount, threadIds.Distinct().Count());
        }

        [Test]
        public void LaunchesThreadsInParallel()
        {
            var startTimes = new List<TimeSpan>();
            var stopWatch = Stopwatch.StartNew();
            Benchmark.Parallel(() =>
            {
                lock (startTimes) startTimes.Add(stopWatch.Elapsed);
                Thread.Sleep(SleepMilliseconds);
            }, ThreadsCount);
            var max = startTimes.Max();
            Assert.Less(max.TotalMilliseconds, ThreadsCount * MaxThreadStartupTimeMilliseconds);
        }

        [Test]
        public void WaitsForAllThreadsToFinish()
        {
            var stopWatch = Stopwatch.StartNew();
            Benchmark.Parallel(() => Thread.Sleep(SleepMilliseconds), ThreadsCount);
            stopWatch.Stop();
            Assert.Less(SleepMilliseconds, stopWatch.Elapsed.TotalMilliseconds);
        }

        [Test]
        public void AggregatesCorrectly()
        {
            var result = Benchmark.Parallel(() => Thread.Sleep(SleepMilliseconds), ThreadsCount);
            var actualDeviation = result.TotalTime.TotalMilliseconds / SleepMilliseconds;
            Assert.Less(actualDeviation, MaxDeviationAllowed);
        }
    }
}